package mbsl.ui;

import mbsl.util.Cache;

import org.apache.commons.lang.builder.HashCodeBuilder;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;


public class BarCreator {
	public BarCreator(Display pDisplay, int pHeight, int pWidth, int pMinRating, int pMaxRating) {
		mHeight = pHeight;
		mWidth = pWidth;
		mDisplay = pDisplay;
		mMinRating = pMinRating;
		mMaxRating = pMaxRating;
		
		if (mCache == null)
			mCache = new Cache<Image>();
	}
	/**
	 * Creates a new BarCreator with minimum- and maximum rating of 1.
	 */
	public BarCreator(Display pDisplay, int pHeight, int pWidth) {
		this(pDisplay, pHeight, pWidth, 0, 1);
	}
	public int getHeight() {
		return mHeight;
	}
	public void setHeight(int pHeight) {
		mHeight = pHeight;
	}
	public int getMinRating() {
		return mMinRating;
	}
	public void setMinRating(int pMinRating) {
		mMinRating = pMinRating;
	}
	public int getMaxRating() {
		return mMaxRating;
	}
	public void setMaxRating(int pMaxRating) {
		mMaxRating = pMaxRating;
	}
	public int getWidth() {
		return mWidth;
	}
	public void setWidth(int pWidth) {
		mWidth = pWidth;
	}
	/**
	 * Returns a filled bar in the passed color.
	 */
	public Image getBar(int pR, int pG, int pB) {
		return getBar(new Color(mDisplay, pR, pG, pB), mMaxRating);
	}
	public Image getBar(int pRating) {
		if (pRating < mMinRating)
			pRating = mMinRating;
		else if (pRating > mMaxRating)
			pRating = mMaxRating;
		
		// Where on the scale is the threshold
		double threshold = (mMaxRating - mMinRating) * GRADIENT_THRESHOLD;

		int[] colour;
		
		if (pRating - mMinRating < threshold) {
			// Where in the range [min, threshold) is this rating
			double grade = (pRating - mMinRating) / (threshold - mMinRating);

			colour = getColour(COLOUR1, COLOUR2, grade);
		} else {
			double grade = (((pRating - threshold) - mMinRating) / ((mMaxRating - threshold) - mMinRating));

			colour = getColour(COLOUR2, COLOUR3, grade);
		}

		return getBar(new Color(mDisplay, colour[0], colour[1], colour[2]), pRating);
	}
	
	public static void main(String[] pArgs) {
		Slider.main(pArgs);
	}
	
	private int[] getColour(int[] pFrom, int[] pTo, double pGrade) {
		int[] reply = new int[pFrom.length];
		
		for (int i = 0; i < pFrom.length; i++)
			reply[i] = (int)(pFrom[i] - (pFrom[i] - pTo[i]) * pGrade);
		
		return reply;
	}
	private Image getBar(Color pColor, int pRating) {
		if (pRating > mMaxRating)
			pRating = mMaxRating;
		
		int filledWidth;

		if (pRating == mMaxRating)
			filledWidth = mWidth;
		else
			filledWidth = (int)((double)mWidth / (mMaxRating - mMinRating) * pRating);

		Specification spec = new Specification(mWidth, mHeight, filledWidth, pColor);
		
		Image icon = mCache.get(spec.hashCode());
		
		if (icon == null) {
			icon = createImage(pColor, filledWidth);
			
			mCache.put(spec.hashCode(), icon);
		}
		
		return icon;
	}
	private Image createImage(Color pColor, int pFilledWidth) {
		Image image = new Image(mDisplay, mWidth, mHeight);
		
		GC brush = new GC(image);

		brush.setBackground(new Color(mDisplay, 255, 255, 255));
		brush.fillRectangle(0, 0, mWidth, mHeight);
		
		brush.setBackground(pColor);
		brush.fillRectangle(0, 0, pFilledWidth, mHeight);

		int steps = (mMaxRating - mMinRating) / ((mMaxRating - mMinRating) / 5);
		double increments = mWidth / (double)(steps);
		brush.setForeground(mDisplay.getSystemColor(SWT.COLOR_WIDGET_BORDER));
		
		for (int i = 1; i < mMaxRating; i++) {
			brush.setAlpha(4);
			brush.drawLine((int)(i * increments) - 1, 0, (int)(i * increments) - 1, mHeight);
			brush.setAlpha(12);
			brush.drawLine((int)(i * increments), 0, (int)(i * increments), mHeight);
			brush.setAlpha(4);
			brush.drawLine((int)(i * increments) + 1, 0, (int)(i * increments) + 1, mHeight);
		}
		
		brush.dispose();

		SmoothV2.applyTexture(mDisplay, image);
		
		return image;
	}
	
	private class Specification {
		private int mFilledWidth;
		private int mHeight;
		private int mWidth;
		private Color mColor;
		
		Specification(int pWidth, int pHeight, int pFilledWidth, Color pColor) {
			mWidth = pWidth;
			mHeight = pHeight;
			mFilledWidth = pFilledWidth > mWidth ? mWidth : pFilledWidth;
			mColor = pColor;
		}
		
		public int hashCode() {
			return new HashCodeBuilder(13, 19)
				.append(mWidth)
				.append(mHeight)
				.append(mFilledWidth)
				.append(mColor.getRed())
				.append(mColor.getGreen())
				.append(mColor.getBlue())
				.toHashCode();
		}
		public boolean equals(Object pObject) {
			if (!(pObject instanceof Specification))
				return false;
			
			return pObject.hashCode() == hashCode();
		}
	}
	/** The percentage at which the colour should stop going from COLOUR1 to COLOUR2, and start going from COLOUR2 to COLOUR3. */
	private static final double GRADIENT_THRESHOLD = 0.7;
	private static final int[] COLOUR1 = {255, 0, 0};
	private static final int[] COLOUR2 = {255, 230, 100};
	private static final int[] COLOUR3 = {100, 230, 100};

	private int mHeight;
	private int mMinRating;
	private int mMaxRating;
	private int mWidth;
	private Display mDisplay;
	private static Cache<Image> mCache;
}
